<?php


namespace App\Logic\Purchase\Form;

use App\Core\BaseListForm;
use App\Core\Exception\DefaultException;
use App\Event\Event;
use App\Logic\Account\CashoutService;
use App\Logic\Member\MemberScoreService;
use App\Model\Account\CashoutModel;
use App\Model\Account\StatisticsModel;
use App\Model\Member\MemberModel;
use App\Model\Member\MemberScoreModel;
use App\Model\Product\ProductModel;
use App\Model\Purchase\PurchaseModel;
use App\Util\CommonHelper;
use App\Util\UserIdentityHelper;
use EasySwoole\ORM\DbManager;
use EasySwoole\RedisPool\RedisPool;

class SubmitPurchaseForm extends BaseListForm
{
    public $id;
    public $amount;
    public $goodsLimit;
    public $created_at;
    public $goods = [];

    /**
     * 校验规则 rules
     * @return array[]
     */
    public function rules()
    {
        return [
            'amount' => ['required|numeric|min:0.01', '订单总价', '订单总价错误'],
        ];
    }

    public function validate()
    {
        //生成验证器，方法自动读取rules方法返回的值数据
        $validator = $this->createValidator();
        if (!($check = $validator->validate($this->getAttributes()))) {
            throw new DefaultException($validator->getError()->__toString(), 600);
        }
        return true;
    }

    public function submit()
    {
        $cache     = RedisPool::defer();
        $cache_key = "purchaseCache:submit";
        if ($cache_data = $cache->get($cache_key)) { //缓存存在
            throw new DefaultException('订单数据生成中,请稍后重试', 600);
        }
        $this->goodsLimit = 20;
        $this->validate();
        /**获取下单商品和数量*/
        $this->distributeGoodsUniformly($this->amount);
        //插入数据
        DbManager::getInstance()->startTransactionWithCount('write');
        try {
            $totalAmount = 0;
            /**要insert的订单数组*/
            $orderData = [];

            foreach ($this->goods as $good) {
                if ($good['goods_quantity'] > 0) {
                    $insertData  = [
                        'id'             => $this->getOrderId(time()),
                        'goods_name'     => $good['goods_name'],
                        'goods_img'      => $good['goods_img'],
                        'goods_price'    => $good['goods_price'],
                        'goods_quantity' => $good['goods_quantity'],
                        'order_amount'   => bcmul($good['goods_price'], $good['goods_quantity'], 2),
                        'order_time'     => date('Y-m-d H:i:s'),
                        'shipped_time'   => self::genTime(PurchaseModel::SHIPPEDTIME, time()), //发货时间
                        'receipt_time'   => self::genTime(PurchaseModel::RECEIPTTIME, time()), //收货时间
                        'created_at'     => date('Y-m-d H:i:s'),
                    ];
                    $orderData[] = $insertData;
                    $totalAmount = bcadd($totalAmount, $insertData['order_amount'], 2);
                }
            }
            if(empty($orderData)){
                throw new DefaultException('没有合适的下单商品', 600);
            }
            //插入数据库
            PurchaseModel::insertMulti($orderData);
            if (!$sta = StatisticsModel::getOne([])) {
                throw new DefaultException('统计数据不存在', 600);
            }
            $staData    = StatisticsModel::create()->connection('write')->lockForUpdate()->get($sta->id);
            $updateData = [
                'available_score'            => bcsub($staData->total_score * StatisticsModel::RATE - $staData->unshipping_purchase_amount - $staData->shipped_purchase_amount, $totalAmount, 2),
                'count_purchase'             => $staData->count_purchase + count($orderData),
                'purchase_amount'            => bcadd($staData->purchase_amount, $totalAmount, 2),
                'count_unshipping_purchase'  => $staData->count_unshipping_purchase + count($orderData),
                'unshipping_purchase_amount' => bcadd($staData->unshipping_purchase_amount, $totalAmount, 2),
            ];
            if ($updateData['available_score'] < 0) {
                throw new DefaultException('金额不足', 600);
            }
            StatisticsModel::updateData($updateData, ['id' => $staData->id]);
            DbManager::getInstance()->commitWithCount('write');

            $returnData = [
                'count'  => count($orderData),
                'amount' => $totalAmount,
            ];

            $result = $this->success($returnData);
            $cache->set($cache_key, date('Y-m-d H:i:s'), 60);
            PurchaseModel::delCache('purchaseCache:');
            return $result;
        } catch (\Throwable $e) {
            DbManager::getInstance()->rollbackWithCount('write');
            CommonHelper::logException($e, '下单错误');
            throw $e;
        }
    }

    /**
     * 订单编号 getOrderNo
     * @return string
     */
    public function getOrderId($time)
    {
        return 'OR' . date('YmdHis', $time) . commonHelper::getRandom('', 8);
    }

    /**
     * 获取指定时间
     */
    public static function genTime(array $dateRange, $startTime = null, $currentDate = null)
    {
        // 获取当前时间戳
        $currentTimestamp = time();

        // 计算x小时后的时间戳
        $startTimestamp = $currentTimestamp + ($dateRange[0] * 3600);

        // 计算x小时后的时间戳
        $endTimestamp = $currentTimestamp + ($dateRange[1] * 3600);

        // 生成随机时间戳
        $randomTimestamp = mt_rand($startTimestamp, $endTimestamp);

        // 转换为DateTime对象
        $randomDateTime = new \DateTime();
        $randomDateTime->setTimestamp($randomTimestamp);

        // 设置时间范围为8:00-22:00
        $randomDateTime->setTime(rand(8, 21), rand(0, 59), rand(0, 59));

        // 格式化输出随机时间
        $randomTime = $randomDateTime->format('Y-m-d H:i:s');

        /**如果设置了开始时间,并且获取的时间小于开始时间,那么重新获取时间*/
        if (!empty($startTime) && strtotime($randomTime) <= $startTime) {
            return self::genTime($dateRange, $startTime, $currentDate);
        }
        /**如果指定了日期,并且获取到的时间不在指定日期,那么重新获取时间*/
        if (!empty($currentDate) && date('Y-m-d', strtotime($randomTime)) != $currentDate) {
            return self::genTime($dateRange, $startTime, $currentDate);
        }
        return $randomTime;
    }

    function distributeGoodsUniformly($totalAmount)
    {
        $this->getGoods();
        $goods = $this->goods;
        usort($goods, function ($a, $b) {
            return $a['goods_price'] <=> $b['goods_price'];
        });

        $result          = [];
        $remainingAmount = $totalAmount;

        // 初始化每个商品的数量为 0
        foreach ($goods as $good) {
            $result[$good['id']] = 0;
        }

        // 计算基准数量
        $averageQuantity = intval($totalAmount / array_sum(array_column($goods, 'goods_price')));
        $averageQuantity = max(1, $averageQuantity); // 至少分配 1 个

        // 分配基准数量
        foreach ($goods as &$good) {
            $price       = $good['goods_price'];
            $maxQuantity = min($averageQuantity, intval($remainingAmount / $price));
            if ($maxQuantity > 0) {
                $result[$good['id']] += $maxQuantity;
                $remainingAmount     -= $maxQuantity * $price;
            }
        }

        // 分配剩余金额
        while ($remainingAmount > 0) {
            $allocated = false;

            foreach ($goods as &$good) {
                $price = $good['goods_price'];
                if ($price <= $remainingAmount) {
                    $result[$good['id']] += 1;
                    $remainingAmount     -= $price;
                    $allocated           = true;
                }
            }

            if (!$allocated) {
                break;
            }
        }
        foreach ($this->goods as &$good) {
            $good['goods_quantity'] = $result[$good['id']];
        }
    }

    /**取出足够数量的商品*/
    public function getGoods()
    {
        $this->goods = [];
        $result      = ProductModel::makeQueryModel()->order('RAND()', 'asc')->field('id, goods_name, goods_img, goods_price')->limit($this->goodsLimit)->all();
        foreach ($result as $key => $value) {
            $this->goods[] = $value->toArray();
        }
    }
}
